/*
 * Copyright 2017 ZZJR All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// Created on 2017年2月4日
// $Id$

package com.vdong.redis.data.core;

import com.alibaba.fastjson.TypeReference;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.vdong.redis.data.core.json.FastJsonRedisTemplate;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Description: druid connect test
 *
 * @author <a href="mailto:yrh@qtz360.com">yrh</a>
 * @Date Create on 2018年07月27日
 * @since version1.0 Copyright 2018 QTZ All Rights Reserved.
 */
@Component
public class RedisOperatorImpl extends AbstractRedisOperator {

    private FastJsonRedisTemplate<String, Object> template;

    @Resource
    public void setFastJsonRedisTemplate(FastJsonRedisTemplate<String, Object> template) {
        this.template = template;
    }

    // ---------------------key/value

    /**
     * key/value操作，根据key获得value
     *
     * @param key String
     * @return Optional<String> Optional.absent() means null
     */
    public Optional<String> getImpl(String key) {
        if (!Optional.fromNullable(key).isPresent()) {
            return Optional.absent();
        }
        return (Optional<String>) getOptional(template.opsForValue().get(key, String.class));
    }

    /**
     * key/value操作，根据key获得value，并转换成指定class
     *
     * @param key
     * @param clz
     * @return
     */
    public <T> Optional<T> getImpl(String key, Class<T> clz) {
        if (!Optional.fromNullable(key).isPresent()) {
            return Optional.absent();
        }
        return getOptional((T) template.opsForValue().get(key, clz));
    }

    /**
     * key/value操作，根据key获得value的列表，并转换成指定class
     *
     * @param key String
     * @param clz Reference E List<E>
     * @return Optional<List       <       E>> Optional.absent() means null
     */
    public <E> Optional<List<E>> getForListImpl(String key, Class<E> clz) {
        if (!Optional.fromNullable(key).isPresent()) {
            return Optional.absent();
        }
        return getOptional(template.opsForValue().getList(key, clz));
    }

    /**
     * key/value操作，根据key设置value
     *
     * @param key   String
     * @param value Object
     */
    public void setImpl(String key, Object value) {
        template.opsForValue().set(key, value);
    }

    /**
     * key/value操作，根据key设置value,设置过期时间
     *
     * @param key
     * @param value
     * @param second
     */
    public void setImpl(String key, Object value, long seconds) {
        template.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);
    }

    /**
     * key/value操作，根据key设置value,设置过期时间
     *
     * @param key
     * @param value
     * @param timeout
     * @param unit
     */
    public void setImpl(String key, Object value, long timeout, TimeUnit unit) {
        template.opsForValue().set(key, value, timeout, unit);
    }

    /**
     * key/value操作，根据key设置value,只有redis中key不存在时，才插入值
     *
     * @param key   must not be {@literal null}.
     * @param value must not be {@literal null}.
     * @return
     * @see <a href="http://redis.io/commands/setnx">Redis Documentation:
     * SETNX</a>
     */
    public Optional<Boolean> setNxImpl(String key, Object value) {
        if (!Optional.fromNullable(key).isPresent()) {
            return Optional.absent();
        }
        return getOptional(template.opsForValue().setIfAbsent(key, value));
    }

    /**
     * key/value操作，根据key设置value,只有redis中key不存在时，才插入值，并设置过期时间
     *
     * @param key     String
     * @param value   object
     * @param seconds timeout seconds
     */
    public void setExImpl(String key, Object value, long seconds) {
        template.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);
    }

    /**
     * key/value操作，根据key设置value,只有redis中key不存在时，才插入值，并设置过期时间
     *
     * @param key     String
     * @param value   object
     * @param timeout timeout
     * @param unit    time unit
     */
    public void setExImpl(String key, Object value, long timeout, TimeUnit unit) {
        template.opsForValue().set(key, value, timeout, unit);
    }

    /**
     * 自增1
     *
     * @param key String
     * @return the incred value
     */
    public Optional<Long> incrImpl(String key) {
        return incrBy(key, 1);
    }

    /**
     * 自增指定值
     *
     * @param key   String
     * @param delta increment number
     * @return eht incred value
     */
    public Optional<Long> incrByImpl(String key, final long delta) {
        if (!Optional.fromNullable(key).isPresent()) {
            return Optional.absent();
        }
        return getOptional(template.opsForValue().increment(key, delta));
    }

    /**
     * 自减指定值
     *
     * @param key
     * @param delta
     * @return
     */
    public Optional<Long> decrByImpl(String key, final long delta) {
        return incrBy(key, -delta);
    }

    // -------------------hash---------------------

    /**
     * 获得map的值
     *
     * @param key   Object
     * @param field String
     * @return the value associated with field, or nil when field is not present
     * in the hash or key does not exist.
     */
    public <T> Optional<T> hgetImpl(String key, Object field) {
        if (!Optional.fromNullable(key).isPresent()) {
            return Optional.absent();
        }
        if (!Optional.fromNullable(field).isPresent()) {
            return Optional.absent();
        }
        return (Optional<T>) getOptional(template.opsForHash().get(key, field));
    }

    /**
     * 获得map的值
     *
     * @param key
     * @param field
     * @param clz   the value reference T
     * @return the value associated with field, or nil when field is not present
     * in the hash or key does not exist.
     */
    public <T> Optional<T> hgetImpl(String key, Object field, Class<T> clz) {
        if (!Optional.fromNullable(key).isPresent()) {
            return Optional.absent();
        }
        if (!Optional.fromNullable(field).isPresent()) {
            return Optional.absent();
        }
        return getOptional(template.opsForHash().get(key, field, clz));
    }

    /**
     * 获得map的值，list形式
     *
     * @param key
     * @param field
     * @param clz
     * @return
     */
    public <E> Optional<List<E>> hgetForListImpl(String key, Object field, Class<E> clz) {
        if (!Optional.fromNullable(key).isPresent()) {
            return Optional.absent();
        }
        if (!Optional.fromNullable(field).isPresent()) {
            return Optional.absent();
        }
        template.setConvertType(new TypeReference<List<E>>(clz) {
        });
        HashOperations<String, Object, List<E>> hashOps = template.opsForHash();
        return getOptional(hashOps.get(key, field));
    }

    /**
     * 设置map的值
     *
     * @param key
     * @param field
     * @param value
     */
    public void hsetImpl(String key, Object field, String value) {
        if (!Optional.fromNullable(key).isPresent()) {
            return;
        }
        if (!Optional.fromNullable(field).isPresent()) {
            return;
        }
        if (!Optional.fromNullable(value).isPresent()) {
            return;
        }
        HashOperations<String, Object, Object> hashOps = template.opsForHash();
        hashOps.put(key, field, value);
    }

    /**
     * 设置map的值
     *
     * @param key
     * @param field
     * @param value
     */
    public void hsetImpl(String key, Object field, Object value) {
        if (!Optional.fromNullable(key).isPresent()) {
            return;
        }
        if (!Optional.fromNullable(field).isPresent()) {
            return;
        }
        if (!Optional.fromNullable(value).isPresent()) {
            return;
        }
        template.opsForHash().put(key, field, value);
    }

    /**
     * 设置map的多个值
     *
     * @param key
     * @param hashMap
     */
    public void hmsetImpl(String key, Map<Object, Object> hashMap) {
        if (!Optional.fromNullable(key).isPresent()) {
            return;
        }
        if (!Optional.fromNullable(hashMap).isPresent()) {
            return;
        }
        Map<String, String> put_map = Maps.newHashMapWithExpectedSize(hashMap.size());
        template.opsForHash().putAll(key, put_map);
    }

    /**
     * 删除map的值
     *
     * @param key
     * @param field
     * @return
     */
    public Optional<Long> hdelImpl(String key, String field) {
        if (!Optional.fromNullable(key).isPresent()) {
            return Optional.absent();
        }
        if (!Optional.fromNullable(field).isPresent()) {
            return Optional.absent();
        }
        return getOptional(template.opsForHash().delete(key, field));
    }

    /**
     * 设置过期时间
     *
     * @param key     String
     * @param seconds timeout
     * @return
     */
    public Optional<Boolean> expireImpl(String key, long seconds) {
        return expire(key, seconds, TimeUnit.SECONDS);
    }

    /**
     * 设置生存时长
     *
     * @param key     String
     * @param timeout timeout
     * @param unit    time unit
     * @return
     */
    public Optional<Boolean> expireImpl(String key, long timeout, TimeUnit unit) {
        if (!Optional.fromNullable(key).isPresent()) {
            return Optional.of(false);
        }
        if (!Optional.fromNullable(timeout).isPresent()) {
            return Optional.of(false);
        }
        return getOptional(template.expire(key, timeout, unit));
    }

    /**
     * 设置过期时间
     *
     * @param key
     * @param date
     * @return
     */
    public Optional<Boolean> expireAtImpl(String key, Date date) {
        if (!Optional.fromNullable(key).isPresent()) {
            return Optional.of(false);
        }
        if (!Optional.fromNullable(date).isPresent()) {
            return Optional.of(false);
        }
        return getOptional(template.expireAt(key, date));
    }

    /**
     * 删除指定的键
     *
     * @param key
     */
    public void delImpl(String key) {
        if (!Optional.fromNullable(key).isPresent()) {
            return;
        }
        template.delete(key);
    }

    /**
     * 删除指定的多个键
     *
     * @param keys
     */
    public void delImpl(String... keys) {
        List<String> list = Lists.newArrayList();
        for (String string : keys) {
            if (Optional.fromNullable(string).isPresent()) {
                list.add(string);
            }
        }
        if (list.size() == 0) {
            return;
        }
        template.delete(list);
    }

    private <T> Optional<T> getOptional(T obj) {
        if (!Optional.fromNullable(obj).isPresent()) {
            return Optional.absent();
        }
        return Optional.fromNullable(obj);
    }

    /**
     * 获得RedisTemplate
     *
     * @return
     */

    public RedisTemplate<String, Object> getTemplate() {
        return template;
    }

    /**
     * 获得FastJsonRedisTemplate
     *
     * @return
     */

    public FastJsonRedisTemplate<String, Object> getFastJsonRedisTemplate() {
        return template;
    }

    /**
     * 忽略redis的异常，如果是，则执行redis下一条命令错误后，将不会抛出异常，但会打印日志 注意，redis命令执行后，会自动失效
     */

    public void ignoreRedisException() {
        template.ignoreRedisException();
    }

    /**
     * 是否存在该键值
     *
     * @param key
     * @return
     */
    public Optional<Boolean> hasKeyImpl(String key) {
        return getOptional(template.hasKey(key));
    }
}